import os.path
import gtk
import gtk.glade
import gnomevfs
import urllib
import nautilus
from kiwi.ui.widgets.combobox import ComboBox as KiwiComboBox

def orphanize(widget):
    widget.get_parent().remove(widget)

MISSING = object()

def initialize_widgets(handle, result, op_data, data):
    try:
        rsrc, ps = op_data
        page, ns_props = data
        page.resource = rsrc
        page.property_sets.append(ps)
        for wname, widget, ns, pname in ns_props:
            prop = ps.getProperty(pname, MISSING)
            if prop is MISSING:
                raise ValueError, (ps, ns, pname)
            vocab = None
            if isinstance(widget, (gtk.ComboBox, KiwiComboBox)):
                vocab = rsrc.getVocab(ns, pname)
            set_widget_value(widget, unicode(prop.getValue()), vocab)
            # Only set attribute after value has been set, otherwise
            # it will execute the 'change' event.
            setattr(widget, 'prop', prop)
    finally:
        # Always end the gtk mainloop, otherwise it will wait forever.
        gtk.main_quit()

def open_cb(handle, exc, data):
    if exc is None:
        page, ns, ns_props = data
        handle.control(operation='plone:propertyset',
                       operation_data=ns,
                       callback=initialize_widgets,
                       callback_data=(page, ns_props))
    else:
        gtk.main_quit()

def get_widget_value(widget):
    if isinstance(widget, gtk.TextView):
        buf = widget.get_buffer()
        return buf.get_text(buf.get_start_iter(), buf.get_end_iter())
    if isinstance(widget, KiwiComboBox):
        return widget.get_selected_data()
    if isinstance(widget, gtk.ComboBox):
        model = widget.get_model()
        idx = widget.get_active()
        if idx > -1:
            return model[idx][1]
        return
    return widget.get_text()

def set_widget_value(widget, text, vocab=None):
    if isinstance(widget, gtk.TextView):
        buf = widget.get_buffer()
        return buf.set_text(text)
    if isinstance(widget, KiwiComboBox):
        if vocab is not None:
            widget.prefill([(term.label, term.value) for term in vocab])
            if text:
                widget.select_item_by_data(text)
    elif isinstance(widget, gtk.ComboBox):
        if vocab is not None:
            store = gtk.ListStore(str, str)
            cell = gtk.CellRendererText()
            widget.pack_start(cell)
            widget.add_attribute(cell, 'text', 0)
            active = None
            store.set_sort_column_id(0, 0)
            for idx, term in enumerate(vocab):
                itr = store.append([term.label, term.value])
                if term.value == text:
                    active = itr
            widget.set_model(store)
            if active is not None:
                widget.set_active_iter(active)
        return vocab
    return widget.set_text(text)

class DublinCorePropertyPage(nautilus.PropertyPageProvider):
    glade_file = os.path.join(os.path.dirname(__file__), 'dublincore.glade')
    widget_names = ('http://cmf.zope.org/propsets/dublincore#title',
                    'http://cmf.zope.org/propsets/dublincore#description',
                    'http://cmf.zope.org/propsets/default#discussion',
                    'http://cmf.zope.org/propsets/dublincore#language',
                    'http://cmf.zope.org/propsets/dublincore#contributors',
                    'http://cmf.zope.org/propsets/dublincore#rights')

    def __init__(self):
        assert os.path.exists(self.glade_file)

    def clean(self):
        self.property_sets = []
        self.resource = None
        self.connected = False
        for name in self.widget_names:
            widget = self.tree.get_widget(name)
            setattr(widget, 'prop', None)

    def change_callback(self, widget, event, *args):
        if not self.property_sets:
            return
        if getattr(widget, 'prop', None) is None:
            return
        new_value = get_widget_value(widget)
        widget.prop.setValue(new_value)
        if not self.connected:
            widget.get_toplevel().connect('destroy', self.save_callback)
            self.connected = True

    def save_callback(self, widget, *args):
        if not self.property_sets:
            return
        for pset in self.property_sets:
            pset.commit(self.resource)
        self.clean()
        self.file.invalidate_extension_info()

    def populate_widgets(self):
        by_ns = {}
        for wname, widget in self.widgets.items():
            ns, pname = urllib.splittag(wname)
            ns_props = by_ns.setdefault(ns, [])
            ns_props.append((wname, widget, ns, pname))

        for ns, ns_props in by_ns.items():
            handle = gnomevfs.async.open(self.uri, callback=open_cb,
                                         data=(self, ns, ns_props))
            gtk.main()

    def get_property_pages(self, files):
        if len(files) != 1:
            return

        file = files[0]
        if file.get_uri_scheme() != 'plone':
            return

        self.file = file
        self.uri = file.get_uri()
        self.tree = gtk.glade.XML(self.glade_file)
        self.clean()

        self.property_label = self.tree.get_widget('property_label')
        orphanize(self.property_label)
        self.property_label.show()

        self.vbox = self.tree.get_widget('vbox')
        orphanize(self.vbox)
        self.vbox.show()

        self.widgets = {}
        for name in self.widget_names:
            self.widgets[name] = widget = self.tree.get_widget(name)
            widget.show()
            for evt in ('changed',):
                widget.connect(evt, self.change_callback, widget)

        self.populate_widgets()
        return nautilus.PropertyPage("plone::dublincore",
                                     self.property_label, self.vbox),
